﻿using System;
using System.Collections.Generic;
using System.Text;
using UnityEditor;
using UnityEngine;

namespace PerfAssist.MemoryProfilerAdvanced.Editor.PAContrib
{
    public class MemStats
    {
        public static void ShowTypeStats(MemType mt)
        {
            if (mt == null)
            {
                Debug.LogError("invalid type, ignored.");
                return;
            }

            if (mt.TypeName.EndsWith("System.String")) // this would excludes 'System.String[]'
                ShowStringStats(mt);

            // accumulate by 'referenced by' types
            var referenceMap = new Dictionary<string, HashSet<MemObject>>();
            foreach (var obj in mt.Objects)
            {
                var mo = obj as MemObject;
                if (mo != null && mo._thing != null)
                    foreach (var referencer in mo._thing.referencedBy)
                    {
                        var referencerTypeName = MemUtil.GetGroupName(referencer);
                        HashSet<MemObject> things;
                        if (!referenceMap.TryGetValue(referencerTypeName, out things))
                        {
                            things = new HashSet<MemObject>();
                            referenceMap[referencerTypeName] = things;
                        }

                        things.Add(mo);
                    }
            }

            var lines = new List<KeyValuePair<int, string>>();
            foreach (var p in referenceMap)
            {
                var objects = p.Value;

                var totalSize = 0;
                foreach (var obj in objects)
                    totalSize += obj.Size;
                lines.Add(new KeyValuePair<int, string>(objects.Count,
                    string.Format("<{0, 80}> {1, 10}, {2, 10}", p.Key, objects.Count,
                        EditorUtility.FormatBytes(totalSize))));
            }

            lines.Sort((x, y) => x.Key.CompareTo(y.Key) * -1); // would sort all results from the largest to the smallest

            var sb = new StringBuilder();
            sb.AppendFormat("----- <type: {0}> -----\n", mt.TypeName);
            sb.AppendFormat(" {0} objects ({1}) are referenced by {2} types listed below:\n", mt.Objects.Count,
                mt.SizeLiterally, lines.Count);
            sb.AppendFormat("-----------------------\n");
            sb.AppendFormat("<{0, 80}> {1, 10}, {2, 10}\n", "type", "count", "size");
            sb.AppendFormat("<{0, 80}> {1, 10}, {2, 10}\n", "----", "-----", "----");
            foreach (var line in lines)
                sb.AppendLine(line.Value);
            Debug.Log(sb.ToString());
        }

        public static void ShowStringStats(MemType mt)
        {
            var counter = new Dictionary<string, int>();

            var pathCount = 0;
            var winPathCount = 0;
            var sb = new StringBuilder();
            foreach (var obj in mt.Objects)
            {
                var mo = obj as MemObject;
                if (mo != null)
                {
                    if (mo.InstanceName.Split('/').Length >= 3) pathCount++;
                    if (mo.InstanceName.Split('\\').Length >= 3)
                    {
                        sb.AppendFormat("  {0}\n", mo.InstanceName);
                        winPathCount++;
                    }

                    if (counter.ContainsKey(mo.InstanceName))
                        counter[mo.InstanceName]++;
                    else
                        counter.Add(mo.InstanceName, 1);
                }
            }

            Debug.LogFormat("path: {0}, winPath: {1}", pathCount, winPathCount);
            Debug.LogFormat("all win paths: \n{0}", sb);

            var logAddCount = 0;
            var logRemoveCount = 0;
            long logAddSize = 0;
            long logRemoveSize = 0;

            var lines = new List<KeyValuePair<int, string>>();
            foreach (var p in counter)
            {
                if (p.Value >= 2) lines.Add(new KeyValuePair<int, string>(p.Value, p.Key));

                var strKey = "DataType";
                //string strKey = "System.Int32";
                if (p.Key.Contains(strKey))
                {
                    if (p.Key.Contains("{added}"))
                    {
                        logAddCount++;
                        logAddSize += p.Key.Length;
                    }
                    else
                    {
                        logRemoveCount++;
                        logRemoveSize += p.Key.Length;
                    }
                }
            }

            lines.Sort((x, y) => x.Key.CompareTo(y.Key) * -1); // would sort all results from the largest to the smallest
            if (lines.Count > 100) lines.RemoveRange(100, lines.Count - 100);

            Debug.Log("----- string repetitions -----");
            foreach (var line in lines)
                try
                {
                    Debug.LogFormat(" {0, 5} {1}\n", line.Key, line.Value);
                }
                catch (Exception ex)
                {
                    Debug.LogFormat(" {0, 5} {1}: {2}\n", line.Key, "<invalid string>", ex.Message);
                }

            Debug.LogFormat("------------ add count:{0} size:{1} , remove count:{2} size:{3}  dif:{4}---------------\n",
                logAddCount, logAddSize, logRemoveCount, logRemoveSize, logAddSize - logRemoveSize);
        }
    }
}